package com.atguigu.encryption.service.impl;

import com.atguigu.encryption.mapper.*;
import com.atguigu.encryption.param.AsymmetricDecryptParam;
import com.atguigu.encryption.param.AsymmetricEncryptParam;
import com.atguigu.encryption.param.PageParam;
import com.atguigu.encryption.pojo.*;
import com.atguigu.encryption.service.SymmetricService;
import com.atguigu.encryption.utils.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Base64;
import java.util.List;
import java.util.Map;

@Service
@Slf4j
public class SymmetricServiceImpl implements SymmetricService {

    @Autowired
    private EncryptMapper encryptMapper;

    @Autowired
    private DecryptMapper decryptMapper;

    @Autowired
    private KeysMapper keysMapper;

    @Autowired
    private AsymmetricEncryptMapper asymmetricEncryptMapper;

    @Autowired
    private AsymmetricDecryptMapper asymmetricDecryptMapper;

    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    public static final String []TYPE ={"AES","SHA256","SM2","SM3","SM4","MD5","DES","SHA1"};

    /**
     * 插入数据前，先判断是否存在；
     * @param plaintext
     * @param password
     * @param method
     * @return
     */
    private R checkExistingData(String plaintext, String password, String method) {
            QueryWrapper<Encryption> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("plaintext", plaintext)
                    .eq("method", method);

            if (password != null) {
                queryWrapper.eq("password", password);
            }

            List<Encryption> existingData = encryptMapper.selectList(queryWrapper);

            if (!existingData.isEmpty()) {
                return R.ok("数据库中已存在", existingData.get(0).getCiphertext());
            } else {
                return null;
            }

    }
    @Override
    public R desEncrypt(String plaintext, String password) {
        try {

            // 检查是否存在相同数据
            R existingDataResult = checkExistingData(plaintext, password, "DES");
            if (existingDataResult != null) {
                return existingDataResult;
            }

            String ciphertext = Des.encrypt(plaintext, password);

            Encryption encryption = new Encryption(plaintext, ciphertext, password, "DES");

            int rows = encryptMapper.insert(encryption);
            if (rows == 0) {
                return R.fail("添加数据库失败");
            }

            // 将数据写入Redis的哈希数据结构
//            HashOperations<String, String, String> hashOperations = redisTemplate.opsForHash();
//            hashOperations.put(Plain_KEY, plaintext, ciphertext);  // 将明文作为key，密文作为value
//            hashOperations.put(TYPE, plaintext, "DES");  // 将加密类型作为field

            // TODO redis键值对
            // 也可以直接使用redis中的String，进行键值对即可
            // 将数据写入Redis
            String redisKey = TYPE[6] +":" + plaintext + ":" + password;  // 构造Redis中的Key
            redisTemplate.opsForValue().set(redisKey, ciphertext);  // 将密文存储到对应的Key

            return R.ok("转换成功", ciphertext);

        } catch (Exception e2) {
            e2.printStackTrace();
            return R.fail("转换失败");
        }
    }


    @Override
    public R desDecrypt(String ciphertext, String password) {
        try {


            String plaintext = Des.decrypt(ciphertext, password);

            Decryption decryption = new Decryption(ciphertext, plaintext, password, "DES");


            int rows = decryptMapper.insert(decryption);
            if (rows == 0) {
                return R.fail("添加数据库失败");
            }

            return R.ok("转换成功", plaintext);

        } catch (Exception e2) {
            e2.printStackTrace();
            return R.fail("转换失败");
        }
    }

    @Override
    public R aesEncrypt(String plaintext, String password) {
        try {
            // 检查是否存在相同数据，鉴别问题
            R existingDataResult = checkExistingData(plaintext, password, "AES");
            if (existingDataResult != null) {
                return existingDataResult;
            }

            String ciphertext = AESUtil.encrypt(plaintext, password);
            log.info("ciphertext:{}",ciphertext);

            Encryption encryption = new Encryption(plaintext, ciphertext, password, "AES");

            int rows = encryptMapper.insert(encryption);
            if (rows == 0) {
                return R.fail("添加数据库失败");
            }
            log.info("加密的数据为：{}",rows);

            // 将数据写入Redis
            String redisKey = TYPE[0] +":" + plaintext + ":" + password; // 构造Redis中的Key
            redisTemplate.opsForValue().set(redisKey, ciphertext);  // 将密文存储到对应的Key

            return R.ok("加密转换成功", ciphertext);

        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("转换失败");
        }
    }

    @Override
    public R aesDecrypt(String ciphertext, String password) {
        try {
            String plaintext = AESUtil.decrypt(ciphertext, password);

            Decryption decryption = new Decryption(ciphertext, plaintext, password, "AES");

            int rows = decryptMapper.insert(decryption);
            if (rows == 0) {
                return R.fail("添加数据库失败");
            }
            log.info("解密的数据为：{}",rows);
            return R.ok("解密转换成功", plaintext);

        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("转换失败");
        }
    }

    /**
     * SM4加密
     * @param plaintext
     * @param password
     * @return
     */
    @Override
    public R sm4Encrypt(String plaintext, String password) {
//
        try {
            // 检查是否存在相同数据
            R existingDataResult = checkExistingData(plaintext, password, "SM4");
            if (existingDataResult != null) {
                return existingDataResult;
            }


            // 假设 SM4Util.encryptSM4 方法接受 byte[] key 和 byte[] data
            byte[] keyBytes = password.getBytes(); // 假设 password 是字符串类型的密钥，将其转换为字节数组
            log.info("key长度为：{}",keyBytes.length);
            byte[] dataBytes = plaintext.getBytes(); // 将明文数据转换为字节数组


            String ciphertext = SM4Util.encryptSM4(dataBytes, keyBytes); // 使用 SM4Util 进行加密,将加密后的字节数组转换为字符串

            log.info("ciphertext: {}", ciphertext);

            Encryption encryption = new Encryption(plaintext, ciphertext, password, "SM4");

            int rows = encryptMapper.insert(encryption);
            if (rows == 0) {
                return R.fail("添加数据库失败");
            }
            log.info("加密的数据为：{}", rows);

            // 将数据写入Redis
            String redisKey = TYPE[4] +":" + plaintext + ":" + password; // 构造Redis中的Key
            redisTemplate.opsForValue().set(redisKey, ciphertext);  // 将密文存储到对应的Key

            return R.ok("加密转换成功", ciphertext);

        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("转换失败");
        }

    }

    /**
     * SM4解密
     * @param plaintext
     * @param password
     * @return
     */
    @Override
    public R sm4Decrypt(String plaintext, String password) {
//
        try {
            // 假设 SM4Util.encryptSM4 方法接受 byte[] key 和 byte[] data
            byte[] keyBytes = password.getBytes(); // 假设 password 是字符串类型的密钥，将其转换为字节数组
            log.info("key长度为：{}",keyBytes.length);
            byte[] dataBytes = plaintext.getBytes(); // 将明文数据转换为字节数组


            String ciphertext = SM4Util.decryptSM4(dataBytes, keyBytes); // 使用 SM4Util 进行加密,将加密后的字节数组转换为字符串

            log.info("ciphertext: {}", ciphertext);

            Decryption decryption = new Decryption(plaintext, ciphertext, password, "SM4");

            int rows = decryptMapper.insert(decryption);
            if (rows == 0) {
                return R.fail("添加数据库失败");
            }
            log.info("解密的数据为：{}", rows);
            return R.ok("转换成功", ciphertext);

        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("转换失败");
        }
    }

    /**
     * SM2加密，非对称
     * @param asymmetricEncryptParam
     * @return
     * @throws Exception
     */
    @Override
    public R sm2Encrypt(AsymmetricEncryptParam asymmetricEncryptParam) throws Exception {
        try {

            String plaintext = asymmetricEncryptParam.getPlaintext();
            String publicKey = asymmetricEncryptParam.getPublicKey();
            String ciphertext = SM2Util.encrypt(plaintext, publicKey);
            AsymmetricEncryption asymmetricEncryption = new AsymmetricEncryption(plaintext, publicKey, ciphertext, "SM2");
            int rows = asymmetricEncryptMapper.insert(asymmetricEncryption);
            if (rows == 0) {
                return R.fail("添加数据库失败");
            }

            return R.ok("加密成功", ciphertext);
        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("转换失败");
        }
    }

    /**
     * SM2解密。非对称
     * @param asymmetricDecryptParam
     * @return
     * @throws Exception
     */
    @Override
    public R sm2Decrypt(AsymmetricDecryptParam asymmetricDecryptParam) throws Exception {
        try {

            String ciphertext = asymmetricDecryptParam.getCiphertext();
            String privateKey = asymmetricDecryptParam.getPrivateKey();
            String plaintext = SM2Util.decrypt(ciphertext, privateKey);

            AsymmetricDecryption asymmetricDecryption = new AsymmetricDecryption(ciphertext, privateKey, plaintext, "SM2");
            int rows = asymmetricDecryptMapper.insert(asymmetricDecryption);
            if (rows == 0) {
                return R.fail("添加数据库失败");
            }

            return R.ok("解密成功", plaintext);
        }catch (Exception e){
            e.printStackTrace();
            return R.fail("转换失败");
        }
    }


    @Override
    public R md5(String plaintext) {
        // 检查是否存在相同数据
        R existingDataResult = checkExistingData(plaintext, "", "AES");
        if (existingDataResult != null) {
            return existingDataResult;
        }

        String ciphertext = MD5.getMD5Code(plaintext);
        Encryption encryption = new Encryption(plaintext, ciphertext, null, "MD5");
        int rows = encryptMapper.insert(encryption);
        if (rows == 0) {
            return R.fail("添加数据库失败");
        }
        return R.ok("转换成功", ciphertext);
    }

    @Override
    public R sha1(String plaintext) {
        // 检查是否存在相同数据
        R existingDataResult = checkExistingData(plaintext, "", "SHA1");
        if (existingDataResult != null) {
            return existingDataResult;
        }


        String ciphertext = SHA1.getSha1(plaintext);
        Encryption encryption = new Encryption(plaintext, ciphertext, null, "SHA1");
        int rows = encryptMapper.insert(encryption);
        if (rows == 0) {
            return R.fail("添加数据库失败");
        }
        return R.ok("转换成功", ciphertext);
    }

    @Override
    public R sha256(String plaintext) {
        // 检查是否存在相同数据
        R existingDataResult = checkExistingData(plaintext, "", "SHA256");
        if (existingDataResult != null) {
            return existingDataResult;
        }


        String ciphertext = SHA256.getSHA256Hash(plaintext);
        Encryption encryption = new Encryption(plaintext,ciphertext,null,"SHA256");
        int rows = encryptMapper.insert(encryption);
        if(rows == 0){
            return R.fail("数据库添加失败");
        }
        log.info("转换后的数据为：{}",ciphertext);

        // 将数据写入Redis
        String redisKey = TYPE[1] +":" + plaintext + ":"; // 构造Redis中的Key
        redisTemplate.opsForValue().set(redisKey, ciphertext);  // 将密文存储到对应的Key

        return R.ok("转换成功",ciphertext);
    }


    @Override
    public R sm3(String plaintext) {
        // 检查是否存在相同数据
        R existingDataResult = checkExistingData(plaintext, "", "ASM3");
        if (existingDataResult != null) {
            return existingDataResult;
        }

        byte[] hashValue = SM3Util.hash(plaintext);
        String base64Ciphertext = Base64.getEncoder().encodeToString(hashValue);

        Encryption encryption = new Encryption(plaintext, base64Ciphertext, null, "SM3");
        int rows = encryptMapper.insert(encryption);
        if (rows == 0) {
            return R.fail("数据库添加失败");
        }
        log.info("转换后的数据为：{}", base64Ciphertext);

        // 将数据写入Redis
        String redisKey = TYPE[3] +":" + plaintext + ":"; // 构造Redis中的Key
        redisTemplate.opsForValue().set(redisKey, base64Ciphertext);  // 将密文存储到对应的Key

        return R.ok("转换成功", base64Ciphertext);
    }

    @Override
    public R getKeys(Integer keySize) {
        // 生成密钥对
        Map<String, Object> keyMap = null;
        try {
            keyMap = RSAUtil.initKey(keySize);
            String publicKey = RSAUtil.getPublicKeyStr(keyMap);
            log.info("公钥:[{}]，长度:[{}]", publicKey, publicKey.length());
            String privateKey = RSAUtil.getPrivateKeyStr(keyMap);
            log.info("私钥:[{}]，长度:[{}]", privateKey, privateKey.length());

            Keys keys = new Keys(keySize, publicKey, privateKey, "RSA");
            int rows = keysMapper.insert(keys);
            if (rows == 0) {
                return R.fail("添加数据库失败");
            }

            return R.ok("转换成功", keys);

        } catch (Exception e) {
            e.printStackTrace();
            return R.fail("生成密钥对失败");
        }

    }

    @Override
    public R getAllKeys(PageParam pageParam) {
        Integer size = pageParam.getSize();
        Integer current = pageParam.getCurrent();
        current = current == null ? 1:current;
        size = size == null ? 5:size;

        QueryWrapper<Keys> queryWrapper = new QueryWrapper<>();
        IPage<Keys> page = new Page<>(current, size);
        page = keysMapper.selectPage(page, queryWrapper);
        List<Keys> keys = page.getRecords();
        long total = page.getTotal();
        if (keys == null || keys.size() == 0) {
            return R.fail("查询失败");
        }

        return R.ok("查询成功", keys, total);
    }

    @Override
    public R rsaEncrypt(AsymmetricEncryptParam asymmetricEncryptParam) {
        String plaintext = asymmetricEncryptParam.getPlaintext();
        String publicKey = asymmetricEncryptParam.getPublicKey();
        String ciphertext = RSAUtil.encrypt1(plaintext, publicKey);
        AsymmetricEncryption asymmetricEncryption = new AsymmetricEncryption(plaintext, publicKey, ciphertext, "RSA");
        int rows = asymmetricEncryptMapper.insert(asymmetricEncryption);
        if (rows == 0) {
            return R.fail("添加数据库失败");
        }

        return R.ok("加密成功", ciphertext);
    }

    @Override
    public R rsaDecrypt(AsymmetricDecryptParam asymmetricDecryptParam) {
        String ciphertext = asymmetricDecryptParam.getCiphertext();
        String privateKey = asymmetricDecryptParam.getPrivateKey();
        String plaintext = RSAUtil.decrypt1(ciphertext, privateKey);

        AsymmetricDecryption asymmetricDecryption = new AsymmetricDecryption(ciphertext, privateKey, plaintext, "RSA");
        int rows = asymmetricDecryptMapper.insert(asymmetricDecryption);
        if (rows == 0) {
            return R.fail("添加数据库失败");
        }

        return R.ok("解密成功", plaintext);
    }

    @Override
    public R rsaGetAllEncryption(PageParam pageParam) {
        Integer size = pageParam.getSize();
        Integer current = pageParam.getCurrent();
        current = current == null ? 1:current;
        size = size == null ? 5:size;

        QueryWrapper<AsymmetricEncryption> queryWrapper = new QueryWrapper<>();
        IPage<AsymmetricEncryption> page = new Page<>(current, size);
        page = asymmetricEncryptMapper.selectPage(page, queryWrapper);
        List<AsymmetricEncryption> asymmetricEncryptions = page.getRecords();
        long total = page.getTotal();
        if (asymmetricEncryptions == null || asymmetricEncryptions.size() == 0) {
            return R.fail("查询失败");
        }
        return R.ok("查询成功", asymmetricEncryptions, total);
    }

    @Override
    public R rsaGetAllDecryption(PageParam pageParam) {
        Integer size = pageParam.getSize();
        Integer current = pageParam.getCurrent();
        current = current == null ? 1:current;
        size = size == null ? 5:size;

        QueryWrapper<AsymmetricDecryption> queryWrapper = new QueryWrapper<>();
        IPage<AsymmetricDecryption> page = new Page<>(current, size);
        page = asymmetricDecryptMapper.selectPage(page, queryWrapper);
        List<AsymmetricDecryption> asymmetricDecryptions = page.getRecords();
        long total = page.getTotal();
        if (asymmetricDecryptions == null || asymmetricDecryptions.size() == 0) {
            return R.fail("查询失败");
        }
        return R.ok("查询成功", asymmetricDecryptions, total);
    }


    /**
     * 查询已经存储的密文
     *
     * @param plaintext
     * @return
     */

//    public R queryCiphertextByPlaintext(String plaintext) {
//
//        String ciphertext = encryptMapper.findCiphertextByPlaintext(plaintext);
//        if(ciphertext != null) {
//            return R.ok("查询到的密文是："+ciphertext);
//        } else {
//            return R.fail("未找到对应的密文");
//        }
//    }
    // TODO 普通查询
    public R queryCiphertext(String plaintext, String password, String method) {
        if(password!=null) {
            QueryWrapper<Encryption> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("plaintext", plaintext)
                    .eq("password", password)
                    .eq("method", method);
            List<Encryption> results = encryptMapper.selectList(queryWrapper);
            if (results.size() == 1) {
                return R.ok(results.get(0).getCiphertext());
            } else if (results.isEmpty()) {
                return R.fail("未找到对应的密文");
            } else {
                throw new IllegalStateException("超过多个密文关系异常...");
            }

        }else{
            return getR(plaintext, method);
        }

    }

    /**
     * 无密码的方式查询，得到最后的密文(工具类)
     * @param plaintext
     * @param method
     * @return
     */
    private R getR(String plaintext, String method) {
        QueryWrapper<Encryption> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("plaintext", plaintext)
                .eq("method", method);
        List<Encryption> results = encryptMapper.selectList(queryWrapper);
        if (results.size() == 1) {
            return R.ok(results.get(0).getCiphertext());
        } else if (results.isEmpty()) {
            return R.fail("未找到对应的密文");
        } else {
            throw new IllegalStateException("超过多个密文关系异常...");
        }
    }

    public R queryCiphertext1(String plaintext, String method) {
        return getR(plaintext, method);
    }

    /**
     * redis 缓存优化
     * @param plaintext
     * @param password
     * @param method
     * @return
     */
    // TODO redis缓存优化
    public R queryCiphertextByRedis(String plaintext, String password, String method) {
        String redisKey = method + ":" + plaintext + ":" + (password != null ? password : "");  // 构造查询的Redis Key
        String ciphertext = redisTemplate.opsForValue().get(redisKey);  // 从Redis中获取密文

        if (ciphertext != null) {
            return R.ok(ciphertext);
        } else if (ciphertext.isEmpty()) {
            return R.fail("未找到对应的密文");
        } else {
            throw new IllegalStateException("超过多个密文关系异常...");
        }

    }

    @Override
    public R getAllEncryption(PageParam pageParam) {
        QueryWrapper<Encryption> queryWrapper = new QueryWrapper<>();
        Integer current = pageParam.getCurrent();
        Integer size = pageParam.getSize();
        current = current == null ? 1:current;
        size = size == null ? 5:size;
        IPage<Encryption> page = new Page<>(current, size);
        page = encryptMapper.selectPage(page, queryWrapper);
        List<Encryption> encryptions = page.getRecords();
        long total = page.getTotal();
        if (encryptions == null || encryptions.size() == 0) {
            return R.fail("查询失败");
        }
        return R.ok("查询成功", encryptions, total);

    }


    @Override
    public R getAllDecryption(PageParam pageParam) {
        QueryWrapper<Decryption> queryWrapper = new QueryWrapper<>();
        Integer current = pageParam.getCurrent();
        Integer size = pageParam.getSize();
        current = current == null ? 1:current;
        size = size == null ? 5:size;
        IPage<Decryption> page = new Page<>(current, size);
        page = decryptMapper.selectPage(page, queryWrapper);
        List<Decryption> decryptions = page.getRecords();
        long total = page.getTotal();
        if (decryptions == null || decryptions.size() == 0) {
            return R.fail("查询失败");
        }
        return R.ok("查询成功", decryptions, total);
    }

}
